﻿using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Folding;
using System;
using System.Collections;
using System.Collections.Generic;

namespace NFox.Pyeditor.Runtime
{
    public class AstTree : IEnumerable<AstTree>
    {

        public static List<NewFolding> Foldings;

        public int RealStart { get; private set; }

        public int Start { get; private set; }
        public int End { get; private set; }

        public string Name { get; private set; }

        List<AstTree> _lst = new List<AstTree>();
        dynamic _tree;
        TextDocument _doc;

        protected void GetBody()
        {
            foreach (var t in _tree.body)
            {
                _lst.Add(AstTree.ParseTree(t, _doc));
            }
        }

        protected void GetDecorators()
        {
            foreach (var t in _tree.decorator_list)
            {
                var stree = AstTree.ParseTree(t, _doc);
                stree._isdecorator = true;
                _lst.Add(stree);
                if (stree.Start < RealStart)
                    RealStart = stree.Start;
            }
        }

        private AstTree(string name, dynamic tree, TextDocument doc)
        {
            _tree = tree;
            Name = name;
            _doc = doc;
            if (name == "Module")
            {
                RealStart = Start = 0;
            }
            else
            {
                RealStart = Start = _doc.GetOffset(tree.lineno, tree.col_offset);
            }
        }

        private void UpdateEndOffset(int endOffset)
        {
            End = Name == "Module" ? endOffset : GetEndOffset(endOffset);
            int i = 0;
            for (; i < _lst.Count - 1;)
                _lst[i].UpdateEndOffset(_lst[++i].RealStart);
            if (i < _lst.Count)
                _lst[i].UpdateEndOffset(End);
        }

        private int GetEndOffset(int nextOffset)
        {
            int i = nextOffset - 1;
            for (; i > Start; i--)
            {
                if (!(char.IsControl(_doc.Text[i]) || char.IsWhiteSpace(_doc.Text[i])))
                    break;
            }
            return i + 1;
        }


        public static AstTree Parse(string txt)
        {
            Application.Engine.Execute("import ast");
            dynamic tree = Application.GetValue("ast").parse(txt);
            try
            {
                var doc = new TextDocument();
                doc.Text = string.Copy(txt);
                var atree = ParseTree(tree, doc);
                atree.UpdateEndOffset(doc.Text.Length);
                return atree;
            }
            catch
            {
                return null;
            }
        }

        protected static AstTree ParseTree(dynamic tree, TextDocument doc)
        {
            Type t = tree.GetType();
            string name = t.Name;
            var atree = new AstTree(name, tree, doc);
            switch (name)
            {
                case "FunctionDef":
                case "ClassDef":
                    atree.GetDecorators();
                    atree.GetBody();
                    break;
                case "Excepthandler":
                case "Interactive":
                case "Expression":
                case "Suite":
                case "For":
                case "While":
                case "If":
                case "Exec":
                case "Tryfinally":
                case "With":
                case "Tryexcept":
                case "Ifexp":
                case "Lambda":
                    atree.GetBody();
                    break;
                case "Module":
                    atree.GetBody();
                    break;
            }
            return atree;
        }

        public IEnumerator<AstTree> GetEnumerator()
        {
            return _lst.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public override string ToString()
        {
            return $"{Name}:{_tree}";
        }

    }



}
